
//Module : Base64Coder.CPP
//Purpose: Implementation for the Base64 encoder / decoder class
//Created: PJN / 20-06-2000
//History: None
//
//Copyright (c) 1998 - 2002 by PJ Naughter.  
//All rights reserved.

//////////////// Includes ////////////////////////////////////////////

#include "base64.h"

//////////////// Macros / Locals /////////////////////////////////////

namespace xos_common
{
    static char	Base64Digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    bool base64::m_Init		= false;
    char base64::m_DecodeTable[256];

#ifndef PAGESIZE
#define PAGESIZE					4096
#endif

#ifndef ROUNDTOPAGE
#define ROUNDTOPAGE(a)			(((a/4096)+1)*4096)
#endif


    ////////////////// Implementation ////////////////////////////////////

    base64::base64() : m_pDBuffer(0), m_pEBuffer(0),
        m_nDBufLen(0),	m_nEBufLen(0)
    {
    }

    base64::~base64()
    {
        if (m_pDBuffer != 0)
        {
            delete [] m_pDBuffer;
            m_pDBuffer = 0;
        }

        if (m_pEBuffer != 0)
        {
            delete [] m_pEBuffer;
            m_pEBuffer = 0;
        }
    }

    const char * base64::DecodedMessage() const 
    { 
        return ( char *) m_pDBuffer;
    }

    const char * base64::EncodedMessage() const
    { 
        return (char *) m_pEBuffer;
    }

    long base64::DecodedMessageSize() const 
    {
        return m_nDDataLen;
    }

    long base64::EncodedMessageSize() const
    { 
        return m_nEDataLen;
    }

    void base64::AllocEncode(unsigned long nSize)
    {
        if (m_nEBufLen < nSize)
        {
            if (m_pEBuffer != 0)
                delete [] m_pEBuffer;

            m_nEBufLen = ROUNDTOPAGE(nSize);
            m_pEBuffer = new char [m_nEBufLen];
        }

        memset( m_pEBuffer, 0, m_nEBufLen );

        m_nEDataLen = 0;
    }

    void base64::AllocDecode(unsigned long nSize)
    {
        if (m_nDBufLen < nSize)
        {
            if (m_pDBuffer != 0)
                delete [] m_pDBuffer;

            m_nDBufLen = ROUNDTOPAGE(nSize);
            m_pDBuffer = new char [m_nDBufLen];
        }

        memset( m_pDBuffer, 0, m_nDBufLen );

        m_nDDataLen = 0;
    }

    void base64::SetEncodeBuffer(const char * pBuffer, unsigned long nBufLen)
    {
        unsigned long	i = 0;

        AllocEncode(nBufLen);
        while(i < nBufLen)
        {
            if (!_IsBadMimeChar(pBuffer[i]))
            {
                m_pEBuffer[m_nEDataLen] = pBuffer[i];
                m_nEDataLen++;
            }
            i++;
        }
    }

    void base64::SetDecodeBuffer(const char * pBuffer, unsigned long nBufLen)
    {
        AllocDecode(nBufLen);

        memcpy( m_pDBuffer, pBuffer, nBufLen);

        m_nDDataLen = nBufLen;
    }

    void base64::Encode(const char * pBuffer, unsigned long nBufLen)
    {
        SetDecodeBuffer(pBuffer, nBufLen);
        AllocEncode(nBufLen * 2);

        TempBucket Raw;
        unsigned long	nIndex	= 0;
        while ((nIndex + 3) <= nBufLen)
        {
            Raw.Clear();
            memcpy(&Raw, m_pDBuffer + nIndex, 3);
            Raw.nSize = 3;
            _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
            nIndex += 3;
            m_nEDataLen	+= 4;
        }

        if (nBufLen > nIndex)
        {
            Raw.Clear();
            Raw.nSize = (unsigned char ) (nBufLen - nIndex);
            memcpy(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex);
            _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
            m_nEDataLen += 4;
        }
    }

    void base64::Decode(const char * pBuffer, unsigned long dwBufLen)
    {
        if (!base64::m_Init)
        {
            _Init();
        }

        SetEncodeBuffer(pBuffer, dwBufLen);
        AllocDecode(dwBufLen);

        unsigned long	nIndex = 0;
        TempBucket Raw;

        while((nIndex + 4) <= m_nEDataLen)
        {
            Raw.Clear();
            Raw.nData[0] = base64::m_DecodeTable[( int )m_pEBuffer[nIndex]];
            Raw.nData[1] = base64::m_DecodeTable[( int )m_pEBuffer[nIndex + 1]];
            Raw.nData[2] = base64::m_DecodeTable[( int )m_pEBuffer[nIndex + 2]];
            Raw.nData[3] = base64::m_DecodeTable[( int )m_pEBuffer[nIndex + 3]];

            if (Raw.nData[2] == 255)
            {
                Raw.nData[2] = 0;
            }

            if (Raw.nData[3] == 255)
            {
                Raw.nData[3] = 0;
            }

            Raw.nSize = 4;
            _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
            nIndex += 4;
            m_nDDataLen += 3;
        }

        // If nIndex < m_nEDataLen, then we got a decode message without padding.
        // We may want to throw some kind of warning here, but we are still required
        // to handle the decoding as if it was properly padded.
        if (nIndex < m_nEDataLen)
        {
            Raw.Clear();

            for (unsigned long i=nIndex; i<m_nEDataLen; i++)
            {
                Raw.nData[i - nIndex] = base64::m_DecodeTable[( int )m_pEBuffer[i]];
                Raw.nSize++;
                if(Raw.nData[i - nIndex] == 255)
                {
                    Raw.nData[i - nIndex] = 0;
                }
            }

            _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
            m_nDDataLen += (m_nEDataLen - nIndex);
        }
    }

    unsigned long base64::_DecodeToBuffer(const TempBucket &Decode, char * pBuffer)
    {
        unsigned long	nCount = 0;
        TempBucket	Data;

        _DecodeRaw(Data, Decode);

        for (int i=0; i<3; i++)
        {
            pBuffer[i] = Data.nData[i];

            if( ( unsigned char )pBuffer[i] != 255)
            {
                nCount++;
            }
        }

        return nCount;
    }


    void base64::_EncodeToBuffer(const TempBucket &Decode, char * pBuffer)
    {
        TempBucket Data;

        _EncodeRaw(Data, Decode);

        for (int i=0; i<4; i++)
        {
            pBuffer[i] = Base64Digits[Data.nData[i]];
        }

        switch (Decode.nSize)
        {
        case 1:
            pBuffer[2] = '=';
        case 2:
            pBuffer[3] = '=';
        }
    }

    void base64::_DecodeRaw(TempBucket &Data, const TempBucket &Decode)
    {
        unsigned char  nTemp;

        Data.nData[0] = Decode.nData[0];
        Data.nData[0] <<= 2;

        nTemp = Decode.nData[1];
        nTemp >>= 4;
        nTemp &= 0x03;
        Data.nData[0] |= nTemp;

        Data.nData[1] = Decode.nData[1];
        Data.nData[1] <<= 4;

        nTemp = Decode.nData[2];
        nTemp >>= 2;
        nTemp &= 0x0F;
        Data.nData[1] |= nTemp;

        Data.nData[2] = Decode.nData[2];
        Data.nData[2] <<= 6;
        nTemp = Decode.nData[3];
        nTemp &= 0x3F;
        Data.nData[2] |= nTemp;
    }

    void base64::_EncodeRaw(TempBucket &Data, const TempBucket &Decode)
    {
        unsigned char  nTemp;

        Data.nData[0] = Decode.nData[0];
        Data.nData[0] >>= 2;

        Data.nData[1] = Decode.nData[0];
        Data.nData[1] <<= 4;
        nTemp = Decode.nData[1];
        nTemp >>= 4;
        Data.nData[1] |= nTemp;
        Data.nData[1] &= 0x3F;

        Data.nData[2] = Decode.nData[1];
        Data.nData[2] <<= 2;

        nTemp = Decode.nData[2];
        nTemp >>= 6;

        Data.nData[2] |= nTemp;
        Data.nData[2] &= 0x3F;

        Data.nData[3] = Decode.nData[2];
        Data.nData[3] &= 0x3F;
    }

    bool base64::_IsBadMimeChar(unsigned char  nData)
    {
        switch(nData)
        {
        case '\r': case '\n': case '\t': case ' ' :
        case '\b': case '\a': case '\f': case '\v':
            return true;
        default:
            return false;
        }
    }

    void base64::_Init()
    {  
        // Initialize Decoding table.

        for ( int i=0; i< 256; i++)
        {
            base64::m_DecodeTable[i] = -2;
        }

        for ( int i=0; i<64; i++)
        {
            base64::m_DecodeTable[( int )Base64Digits[i]]	= (char) i;
            base64::m_DecodeTable[( int )Base64Digits[i]|0x80] = (char) i;
        }

        base64::m_DecodeTable['=']	= -1;
        base64::m_DecodeTable['='|0x80] = -1;
        base64::m_Init = true;
    }

} // xos_common
